--
-- @Author:      name
-- @DateTime:    2018-03-30 23:05:48
-- @Description: 消息的处理

local skynet = require "skynet"
local log = require "Log"
local pbc = require "pbc_helper"
local config = require "configquery"
local NodeMessage = require "NodeMessage"
local sharedata = require "sharedata"
local AssertEx = AssertEx
require "common"


local gate_idx = 1
local SOCKET = {}
local Command = class("Command")

---------------------------------------------------------
-- Private
---------------------------------------------------------
function Command:ctor(message)
	self.svr_id = skynet.getenv("svr_id") --节点名
	self.msg = message	
	self.closeAll = false --关服
	self.gateServices = {} --服务列表
	self.userList = {}
	self.agentList = {} --agent_list[fd] = {}
	self.agentUserList = {} --主要是给断线重连用的
	self.tbSocketFun = {}
	self.tbCloseSocket = {} --要关闭的socket
	self.dbmgr = nil --数据库操作
	self:register()
	self.nodeMessage = NodeMessage.new()
	self.roomConfig = {}
	--清除socket
	skynet.fork(function()
		self:loop()
	end)	
end

--注册本服务里的消息
function Command:register()
	self.msg:registerSelf('start',handler(self,self.start))
	--
	self.msg:registerSelf('socket',handler(self,self.socket))
	self.tbSocketFun.open = handler(self,self.openSocket)
	self.tbSocketFun.close = handler(self,self.closeSocket)
	self.tbSocketFun.error = handler(self,self.errorSocket)
	self.tbSocketFun.warning = handler(self,self.warningSocket)
	self.tbSocketFun.data = handler(self,self.dataSocket)

	--
	self.msg:registerSelf('update_agent_user_list',handler(self,self.updateUserSvrList))
	self.msg:registerSelf('check_user_enter',handler(self,self.checkUserEnter))
	self.msg:registerSelf('kick_player',handler(self,self.kickPlayer))
	self.msg:registerSelf('logout',handler(self,self.logout))
	self.msg:registerSelf('transfer',handler(self,self.transfer))
	self.msg:registerSelf('safe_close',handler(self,self.safeClose))
	self.msg:registerSelf('oper',handler(self,self.oper))

	self.msg:registerSelf('test',handler(self,self.test))
end


function Command:start()
	local proto_file = skynet.getenv("proto_config")
	local pbc_env = pbc.init(proto_file, {'base','gate','game',})
	local svr_config = config.setting_cfg["gatesvrs"][self.svr_id]
	--skynet控制台
	skynet.newservice("debug_console",svr_config.debug_port)
	--数据库
	local tables = {
		{ name="usergame", pk='uid',key='uid' },		
		{ name="userinfo", pk='uid',key='uid' },
		{ name="common_room", pk='id',key='sub_id' },
		{ name="robot_online", pk='uid',key='uid' },
		{ name="private_tables", pk='id',key='tableid' },
	}
	local dbmgr = skynet.uniqueservice("dbmgr_service")
	local noRedis = false --是否使用redis
	skynet.call(dbmgr, "lua", "start", tables,noRedis)
	self.dbmgr = dbmgr
	self:loadConfig()

	--mysql日记操作服务
	local logData = {host = skynet.getenv("mysql_host"),
				  port = skynet.getenv("mysql_port"),				  
				  user = skynet.getenv("mysql_user"),
				  password = skynet.getenv("mysql_pwd"),
				  database = "qicaipoker_log",
				  serviceName = "mysql_log",
				 }
	self.mysqlLog = skynet.newservice("mysql_service")
	skynet.call(self.mysqlLog, "lua", "start", logData)

	--启动socket gate
	local conf = {
			port = svr_config.port,
			maxclient = svr_config.maxclient,
			nodelay = true,
		}		
	local gate = skynet.newservice("gate")
	log.debug('_____________manager open socket gate port:'..conf.port)
	skynet.call(gate, "lua", "open", conf)

	--启动websocket gate
	local wsConf = {
			port = svr_config.port_ws,
			maxclient = svr_config.maxclient,
			nodelay = true,
		}
	local wsgate = skynet.newservice("wsgate")
	log.debug('_____________manager open websocket gate port:'..wsConf.port)
	skynet.call(wsgate, "lua", "open", wsConf)

	--socket消息处理
	local gateSvr
	for i = 1, svr_config.gate_size do
		gateSvr = skynet.newservice("gate_service", skynet.self())
		table.insert(self.gateServices,gateSvr)
		skynet.call(gateSvr, 'lua', 'init', pbc_env)
	end
	--通知广播服
	skynet.call('broadcasting', 'lua', 'init', pbc_env)

end

--人数
function Command:setPlayerNum(add)
	if add then 
		--上线人数增加
		local tb = call_redis({ "hgetall", "online" }, 1)	
		local num = tb.num or 0
		num = tonumber(num)
		send_redis({ "hmset", "online", {num=num+1} }, 1)
		return
	end

	--在线人数减少
	log.debug("#########在线人数减少#######")
	local tb = call_redis({ "hgetall", "online" }, 1)	
	local num = tb.num or 0	
	num = tonumber(num)
	if num > 0 then num = num - 1 end 
	send_redis({"hmset","online",{num=num}},1)

end


---------------------------------------------------------
-- socket
---------------------------------------------------------

--- 用户连接
--- @param fd @class number 连接标识符
--- @param addr @class string 连接地址
--- @param contype@class string 连接类型
function Command:openSocket(gate, fd, addr, contype)
	-- log.info("new client fd:" ..fd .." from :".. addr)
	log.debug("___new client fd:" ..fd .." from :".. addr)

	local service_h = self.gateServices[gate_idx]
	gate_idx = gate_idx + 1
	if gate_idx > #self.gateServices then
		gate_idx = 1
	end
	self.agentList[fd] = {fd = fd, gate = gate, service = service_h, addr = addr, contype = contype}
	local strTtems = string_split(addr, ':')
	addr = strTtems[1]
	skynet.call(service_h, "lua", "start", {
		gate        = gate,
		fd      	= fd,
		addr 		= addr,
		contype     = contype,
	})
	-- self:setPlayerNum(true)
end

function Command:closeSocket(gate, fd)
	-- print("####close socket ",gate,fd)
	self:closeAgent(fd)
end

function Command:errorSocket(gate, fd, msg)
	-- print("####client socket error", fd, msg)
	log.debug("####client socket error", fd, msg)
	self:closeAgent(fd, msg, true)
end

function Command:warningSocket(gate, fd, size)
	-- log.info("client socket warning", fd, size)
end

function Command:dataSocket(gate, fd, msg)
end


function Command:socket(source, cmd, ... )
	-- print("######",source, cmd, ... )
	local f = self.tbSocketFun[cmd]	
	if f then
		f(source, ...)
	end
end
---------------------------------------------------------
-- CMD
---------------------------------------------------------


function Command:closeAgent(fd, msg, err)
    --- 清除连接记录
	local ag = self.agentList[fd]
	if ag then
		log.debug("############## closeAgent ag is _______",ag)
		--更新在线人数
		-- self:setPlayerNum(false)		
		--- 通知代理关闭用户连接
		if ag.service then
			skynet.send(ag.service, "lua", "disconnect", fd, msg, err)
		end

		if ag.gate then
			--skynet.call(ag.gate, "lua", "kick", fd)
			self.tbCloseSocket[ag.gate] = fd
		end
		
		if ag.playerid then
			self.userList[ag.playerid] = nil
			--通知广播服
			skynet.send('broadcasting', "lua", "user_out", fd, ag.playerid)
		end
	else
		--assert(nil,"############closeAgent########")
		-- print("############## closeAgent ag is nil#")
		return
	end
	self.agentList[fd] = nil

end

--玩家所在的服务
function Command:updateUserSvrList(playerid,agent)
	log.debug("_____updateUserSvrList",playerid,agent)
	if not agent then 
		--self.userList[playerid] = nil
		self.agentUserList[playerid] = nil
	else
		self.agentUserList[playerid] = agent
	end
end

--检查重复登入,异地登录
function Command:checkUserEnter(svr, fd, playerid)
	if self.closeAll then 
		return 
	end
	if not playerid then 
		log.error("_____check_user_enter_______playerid is null")
		return
	end
	local agent = self.agentList[fd]
	if not agent then
		log.error("error: agent must not be nil! old_fd: ", old_fd,fd,playerid)
		return
	end	
	local old_fd = self.userList[playerid]
	local old_svr = self.agentUserList[playerid]
	log.debug("__________old_svr__",old_svr,fd,old_fd)
	if old_fd and old_fd ~= fd then		
		self:closeAgent(old_fd, 'remote_login')
	end

	self.userList[playerid] = fd
	agent.playerid = playerid
	if old_svr then 
		--关闭fd时需要用到gate_manger agent.service
		agent.service = old_svr
	else
		self:updateUserSvrList(playerid,svr)
	end

	--通知到广播服建立fd和playerid
	skynet.call('broadcasting','lua', 'user_enter', fd, agent.contype, playerid)
	return old_svr
end

--踢指定玩家
function Command:kickPlayer(fd)
	--关掉socket	
	log.debug("___________kickPlayer__",fd)
	--local old_fd = self.userList[playerid]
	if fd then
		self:closeAgent(fd)		
	else
		log.error("###kickPlayer时 player 不存在，"..fd)
	end
end

--登出
function Command:logout( playerid )	
	--登出不关socket
	if not playerid then return end
	log.debug("_11111111111111_-登出不关socket"..playerid)
	local msg = 'logout'
	local fd = self.userList[playerid]
	if not fd then
		return false
	end
	local ag = self.agentList[fd]
	if ag then
		--- 通知代理关闭用户连接
		if ag.service then
			skynet.send(ag.service, "lua", "disconnect", fd, msg, err)
		end
		
		if ag.playerid then
			--self.userList[ag.playerid] = nil
			--通知广播服
			skynet.send('broadcasting', "lua", "user_out", fd,ag.playerid)
		end
	end	
	self.userList[playerid] = nil
	--self.agentUserList[playerid] = nil
	return true
end

--广播消息
function Command:transfer(cmd, ulist, ...)
	skynet.send('broadcasting','lua','broad_cast',ulist,...)
end

--安全关服，踢所有玩家下线
function Command:safeClose(close,gameid)
	-- print("_______safeClose_____",close,gameid)
	if gameid and gameid > 0 then 
		return 
	end
	self.closeAll = close 
	if close then 
		for k,v in ipairs(self.gateServices) do
			skynet.call(v, "lua", "oper", nil, 'safe_close')
		end			
		for k,v in pairs(self.userList) do 
			self:kickPlayer(v)
		end
	end
end

--对玩家属性的操作
function Command:oper( playerid, ... )
	-- print("###########oper#######",playerid,...)
	if not playerid then
		for k,v in ipairs(self.gateServices) do
			skynet.send(v, "lua", "oper", nil, ...)
		end
	else
		local fd = self.userList[playerid]
		local service
		if fd then			
			local ag = self.agentList[fd]
			if ag then
				service = ag.service
			end
		end
		if not service then 
			service = self.agentUserList[playerid]
		end
		if not service then
			log.error('gate_manager oper() 玩家不在线找不到>>>>>>>',playerid,...)
			--service = self.agentUserList[1]
			return false
		end
		return skynet.call(service, "lua", "oper", playerid, ...)
	end
end

--测试
function Command:test()
	print("___Command_______test____")
end

--加载游戏配置
function Command:loadConfig()
	local sqlStr = "select * from common_room"	
	local data = self.nodeMessage:callService("mysql_service",'execute',sqlStr)
	local configData = {}
	local shardName
	for k,v in pairs(data) do 
		if g_gameType[v.gameid] and next(g_gameType[v.gameid]) then
			shardName = g_gameType[v.gameid].cfgRoom
			configData[v.sub_id] = v
			if configData and next(configData) then 			
				if not self.roomConfig[shardName] then 					
			 		sharedata.new(shardName, configData)		 		
			 		self.roomConfig[shardName] = configData
			    else
			        sharedata.update(shardName, configData)
			        self.roomConfig[shardName] = configData
			    end		
			end	
		end   
		-- print("_________loadConfig___",shardName,self.roomConfig[shardName])
	end
end

function Command:loop()
	while true do 
		skynet.sleep(500)
		--清除socket连接
		for k,v in pairs(self.tbCloseSocket) do 
			skynet.send(k, "lua", "kick", v)
			self.tbCloseSocket[k] = nil
		end
		self:loadConfig()
	end
end



return Command